package lumis.portal.webservice;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;

/**
 * Servlet filter that replaces the part name of the WSDL generated by Axis2, in order to allow .NET clients generate
 * more friendly stub classes.
 * 
 * When generating services WSDL, Axis2 uses 'part1' as name for WSDL parts. .NET stub generator needs that WSDL 
 * parts be named as 'parameters', in order to unwrap parameters of service methods. This filter performs replaces the 
 * part name generated by Axis2 by the name preferred by .NET generator, in order to allow this to create client stub classes
 * with unwrapped parameters, giving an easy time to .NET client developers.
 * 
 * Note that replacing or not the part name, Axis2, differing from .NET, generates client stub classes always with wrapped
 * parameters, reproducing the WSDL wrapped definition. In future versions of Axis2, maybe it will unwrap parameters 
 * automatically, as .NET client stub generator does.
 * 
 * 
 * Example of C# .NET client stub invocation, with unwrapped parameters:
 * 
 * AuthenticationService svc = new AuthenticationService();
 * svc.Url = "http://localhost:8080/portal/webservice/AuthenticationService";
 * string userSessionId = authenticationService.loginUser("username", "password");
 * 
 * 
 * Example of Java Axis2 client stub invocation, with wrapped parameters:
 * 
 * AuthenticationServiceStub.LoginUser loginUserParam = new AuthenticationServiceStub.LoginUser();
 * loginUserParam.setLogin("username");
 * loginUserParam.setPassword("password");
 * AuthenticationServiceStub authenticationService = new AuthenticationServiceStub(null, "http://localhost:8080/portal/webservice/AuthenticationService");
 * AuthenticationServiceStub.LoginUserResponse loginUserResponse = authenticationService.loginUser(loginUserParam); 
 * String userSessionId = loginUserResponse.get_return();
 * 
 * 
 * @version $Revision: 5769 $ $Date: 2007-02-01 12:00:07 -0200 (Thu, 01 Feb 2007) $
 * @since 4.0.9
 */
public class WebServiceWsdlServletFilter implements Filter
{
	private class ResponseWrapper extends HttpServletResponseWrapper 
	{
	    private final ByteArrayOutputStream os;
	    private PrintWriter pw = null;
	    private OutputStreamWriter osw = null;

	    public ResponseWrapper(HttpServletResponse response) 
	    {
	        super(response);
	        os = new ByteArrayOutputStream(0);
	        pw = new PrintWriter(os);
	    }

	    public void writeToResponse() throws IOException
	    {
	    	os.writeTo(getResponse().getOutputStream());
	    }
	    
		public synchronized PrintWriter getWriter() throws UnsupportedEncodingException
	    {
			if (pw == null)
			{
				String characterEncoding = getCharacterEncoding();
				if (characterEncoding == null)
					characterEncoding = "ISO-8859-1";
				osw = new OutputStreamWriter(os, characterEncoding);
	    		pw = new PrintWriter(osw);
			}
	        return pw;
	    }

	    public ByteArrayOutputStream getInternalOutputStream() throws IOException
		{
	    	return os;
		}

		@Override
		public ServletOutputStream getOutputStream() throws IOException
		{
			return new ServletOutputStream()
			{
				public void print(String arg0) throws IOException
				{
					getWriter().print(arg0);
				}

				public void print(boolean arg0) throws IOException
				{
					getWriter().print(arg0);
				}

				public void print(char arg0) throws IOException
				{
					getWriter().print(arg0);
				}

				public void print(int arg0) throws IOException
				{
					getWriter().print(arg0);
				}

				public void print(long arg0) throws IOException
				{
					getWriter().print(arg0);
				}

				public void print(float arg0) throws IOException
				{
					getWriter().print(arg0);
				}

				public void print(double arg0) throws IOException
				{
					getWriter().print(arg0);
				}

				public void println() throws IOException
				{
					getWriter().println();
				}

				public void println(String arg0) throws IOException
				{
					getWriter().println(arg0);
				}

				public void println(boolean arg0) throws IOException
				{
					getWriter().println(arg0);
				}

				public void println(char arg0) throws IOException
				{
					getWriter().println(arg0);
				}

				public void println(int arg0) throws IOException
				{
					getWriter().println(arg0);
				}

				public void println(long arg0) throws IOException
				{
					getWriter().println(arg0);
				}

				public void println(float arg0) throws IOException
				{
					getWriter().println(arg0);
				}

				public void println(double arg0) throws IOException
				{
					getWriter().println(arg0);
				}

				public void close() throws IOException
				{
					os.close();
				}

				public void flush() throws IOException
				{
					os.flush();
				}

				public void write(byte[] b, int off, int len) throws IOException
				{
					os.write(b, off, len);
				}

				public void write(byte[] b) throws IOException
				{
					os.write(b);
				}

				public void write(int b) throws IOException
				{
					os.write(b);
				}				
			};
		}
	}
	
	public void destroy()
	{
	}

	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
	{
		String partNameByAxis2 = "<wsdl:part name=\"part1\"";
		String partNameForDotNet = "<wsdl:part name=\"parameters\"";

		String queryString = ((HttpServletRequest)request).getQueryString();
		if (queryString == null || !queryString.endsWith("wsdl"))
		{
			chain.doFilter(request, response);
		}
		else
		{
			ResponseWrapper wrapper = new ResponseWrapper((HttpServletResponse) response);
			chain.doFilter(request, wrapper);
			
			String characterEncoding = wrapper.getCharacterEncoding();
			if (characterEncoding == null)
				characterEncoding = "ISO-8859-1";
			String originalWsdl = new String(wrapper.getInternalOutputStream().toString(characterEncoding));
			if (originalWsdl.contains(partNameByAxis2))
			{
				String replaceWsdl = originalWsdl.replaceAll(partNameByAxis2, partNameForDotNet);
				response.setContentLength(replaceWsdl.length());
				response.getWriter().write(replaceWsdl);
			}
			else
			{
				wrapper.writeToResponse();
			}
		}
	}

	public void init(FilterConfig arg0) throws ServletException
	{
	}

}
